package com.lx.core;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.lx.CommonResultCode;
import com.lx.MsagesException;
import com.lx.Remessages;
import com.lx.Response;
import com.lx.utils.StringConverEditor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;


import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.lang.reflect.Type;
import java.util.Set;

/**
 * Created by lanyxp on 2021/2/23 11:26
 * 全局统一异常处理&&参数校验
 */
@ControllerAdvice
@Slf4j
public class WebControllerAdvice extends RequestBodyAdviceAdapter implements ResponseBodyAdvice<Object> {

    private static final int maxLongSize = 20000;

    private static final StringConverEditor STRINGCONVEREDITOR = new StringConverEditor();

    @InitBinder
    public void initBinder(ServletRequestDataBinder binder) {
        binder.registerCustomEditor(String.class, STRINGCONVEREDITOR);
    }

    @ExceptionHandler(value = {MsagesException.class})
    @ResponseBody
    public Response OSSExceptionHandler(MsagesException ex) {
        return Remessages.fail(ex.getCode(), ex.getMessage());
    }

    @ExceptionHandler(value = {HttpMessageConversionException.class})
    @ResponseBody
    public Response ExceptionHandler(HttpMessageConversionException ex) {
        if (log.isErrorEnabled()) {
            log.error(ex.getMessage());
        }
        return Remessages.fail(CommonResultCode.PARAMS_DATA_NOT_NULL.getCode(), "请求参数格式或者类型错误");
    }

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    @ResponseBody
    public Response ExceptionHandler(MethodArgumentNotValidException methodArgumentNotValidException) {
        if (log.isErrorEnabled()) {
            log.error(methodArgumentNotValidException.getMessage());
        }
        final FieldError fieldError = methodArgumentNotValidException.getBindingResult().getFieldError();
        if (fieldError == null) {
            return Remessages.fail(CommonResultCode.PARAMS_VALATE_FAIL);
        }
        return Remessages.fail(CommonResultCode.PARAMS_VALATE_FAIL.getCode(), fieldError.getDefaultMessage());
    }

    @ExceptionHandler(value = {Exception.class})
    @ResponseBody
    public Response ExceptionHandler(Exception ex) {
        return Remessages.fail(CommonResultCode.SYSTEM_ERROR);
    }

    /**
     * 参数校验 & 暂时对所有的参数进行校验 return true
     */
    private static final Validator VALIDATOR = Validation.byProvider(HibernateValidator.class).configure().failFast(true).buildValidatorFactory().getValidator();

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

        String uri = null;
        try {
            String req = JSON.toJSONString(body);
            if (StringUtils.isNotEmpty(req) && req.length() >= maxLongSize) {
                body = req.substring(0, maxLongSize) + "....";
            }
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            uri = request.getRequestURI();
            log.info("request url:[{}] >> request body:[{}]", uri, req);
        } catch (Exception e) {
            log.error("request error:[{}] >> uri:[{}] >> body:[{}] >> e:[{}]", uri, body, e);
        }
        Set<ConstraintViolation<Object>> validates = VALIDATOR.validate(body);
        if (validates != null && !validates.isEmpty()) {
            ConstraintViolation<Object> next = validates.iterator().next();
            throw new MsagesException(CommonResultCode.PARAMS_VALATE_FAIL.getCode(), next.getMessage());
        }
        return body;
    }

    @Override
    public Object beforeBodyWrite(Object responses, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        try {
            String responseStr = JSON.toJSONString(responses);
            if (StringUtils.isNotEmpty(responseStr) && responseStr.length() >= maxLongSize) {
                responseStr = responseStr.substring(0, maxLongSize) + "....";
            }
            log.info("response result >> [{}]", responseStr);
        } catch (Exception e) {
            log.error("response error system error:[{}]", e);
        }
        return responses;
    }
}
